/*
 * Abstract class of the FileAssistant with part of the implementation.
 */
package filesystem;

import filesystem.exceptions.*;
import java.io.IOException;
import java.util.*;
import java.util.regex.*;
import model.ModelItem;

/**
 *
 * @author ArieS
 */
public abstract class FileAssistantAbstract implements FileAssistant {

    private String path;
    private String extension;
    private Map headers;
    private int maxID = -1;
    private String title;
    protected boolean loaded;
    protected ArrayList items;

    public FileAssistantAbstract(String path, String extension, String title) {
        this.path = path;
        this.extension = extension;
        String[] headersStr = FileWorker.getHeaders(path, extension);
        headers = this.parseHeaders(headersStr);
        items = new ArrayList();
        loaded = false;
        this.title = title;
    }

    @Override
    public void resetLoaded() {
        this.loaded = false;
    }

    protected String headerToName(String header) {
        String res = header.substring(header.indexOf('_') + 1, header.length());
        res = res.substring(0, res.lastIndexOf(extension));
        return res;
    }

    protected int getMaxID() {
        if (maxID == -1) {
            int tmpVal;
            Set set = headers.entrySet();
            Iterator i = set.iterator();
            maxID = 0;
            while (i.hasNext()) {
                Map.Entry me = (Map.Entry) i.next();
                tmpVal = Integer.parseInt(me.getKey().toString());
                if (tmpVal > maxID) {
                    maxID = tmpVal;
                }
            }
        }
        return maxID;
    }

    protected int getNextID() {
        maxID = getMaxID() + 1;
        return maxID;
    }

    protected final Map parseHeaders(String[] headers) {
        Map map = new HashMap();
        for (int i = 0; i < headers.length; i++) {
            map.put(Integer.parseInt(headers[i].substring(0, headers[i].indexOf('_'))), headers[i]);
        }
        return map;
    }

    protected String getFullPath(String fileName) {
        StringBuilder sb = new StringBuilder();
        sb.append(path);
        sb.append("\\");
        sb.append(fileName);
        return sb.toString();
    }

    protected Map itemToMap(ModelItem mi) {
        Map map = new HashMap();
        map.put("id", mi.getId());
        map.put("name", mi.getName());
        map.put("version", mi.getVersion());
        return map;
    }

    protected abstract ModelItem mapToItem(Map map);

    protected String generateFileName(String name, int id) {
        StringBuilder sb = new StringBuilder();
        sb.append(id);
        sb.append("_");
        sb.append(name);
        return sb.toString();
    }

    // return ID of the element which already exists
    protected int isExists(ModelItem item) throws IOException {
        if (!loaded) {
            getItems();
        }
        int i = 0, len = items.size();
        while (i < len && !(item.equals((items.get(i))))) {
            boolean isExist = item.equals((items.get(i)));
            i++;
        }
        if (i != len) {
            return ((ModelItem) items.get(i)).getId();
        }
        return -1;
    }

    @Override
    public void removeItem(int id) throws IOException {
        FileWorker.deleteFile(getFullPath(headers.get(id).toString()));
        resetLoaded();
        Assistants.tFA.resetLoaded();
        headers.remove(id);
    }

    @Override
    public void setItem(ModelItem item) throws IOException {
        Map map = itemToMap(item);
        removeItem(item.getId());
        headers.put(item.getId(), generateFileName(item.getName(), item.getId()) + extension);
        FileWorker.saveFile(map, getFullPath(headers.get(item.getId()).toString()), title);
        resetLoaded();
        Assistants.tFA.resetLoaded();  
    }

    @Override
    public ModelItem getItem(int id) throws IOException {
        Map map = FileWorker.loadFile(getFullPath(headers.get(id).toString()));
        return mapToItem(map);
    }

    @Override
    public void addItem(ModelItem item) throws IOException {
        int exID = isExists(item);
        if (exID != -1) {
            item.setId(exID);
            return;
        }
        item.setId(getNextID());
        String fileName = generateFileName(item.getName(), item.getId());
        fileName += extension;
        headers.put(item.getId(), fileName);
        Map map = itemToMap(item);
        FileWorker.saveFile(map, getFullPath(fileName), title);
        loaded = false;
    }

    @Override
    public ArrayList getItems(String name) throws IOException {
        ArrayList result = new ArrayList();
        Pattern pattern = Pattern.compile(name);
        Matcher matcher;
        if (loaded) {
            int i = 0;
            int size = items.size();
            ModelItem tmpModelItem;
            while (i < size) {
                tmpModelItem = (ModelItem) items.get(i);
                matcher = pattern.matcher(tmpModelItem.getName().toLowerCase());
                if (matcher.matches()) {
                    result.add(tmpModelItem);
                }
                i++;
            }
            return result;
        }
        Set set = headers.entrySet();
        Map map;
        items.clear();
        Iterator i = set.iterator();
        while (i.hasNext()) {
            Map.Entry me = (Map.Entry) i.next();
            map = FileWorker.loadFile(getFullPath(me.getValue().toString()));
            matcher = pattern.matcher(headerToName(me.getValue().toString()));
            if (matcher.matches()) {
                result.add(mapToItem(map));
            }
            items.add(mapToItem(map));
        }
        loaded = true;
        return result;
    }

    @Override
    public ArrayList getItems() throws IOException {
        if (!loaded) {
            Set set = headers.entrySet();
            Map map;
            items.clear();
            Iterator i = set.iterator();
            while (i.hasNext()) {
                Map.Entry me = (Map.Entry) i.next();
                map = FileWorker.loadFile(getFullPath(me.getValue().toString()));
                try {
                    items.add(mapToItem(map));
                } catch (InvalidModelItemException ex) {
                    //Skip ^^
                }
            }
            loaded = true;
        }
        return (ArrayList) items.clone();
    }

    @Override
    public ArrayList getItems(Map map) throws IOException {
        if (!loaded) {
            this.getItems();
        }
        return this.getItems(map, items);
    }

    @Override
    public ArrayList getItems(Map map, ArrayList al) throws IOException {
        ArrayList<Map> restItems = (ArrayList<Map>) al.clone();
        Pattern pattern;
        Matcher matcher = null;
        ModelItem tmpModelItem;
        int i = 0;
       
        while (i < restItems.size()) {
            for (Object ob : map.entrySet()) {
                Map.Entry men = (Map.Entry) ob;
                for (Object o : ((Map)men.getValue()).entrySet()) {
                    Map.Entry me = (Map.Entry) o;
                    if (me.getValue() == null) {
                        continue;
                    }
                    pattern = Pattern.compile(String.valueOf(me.getValue()));
                    tmpModelItem = (ModelItem) restItems.get(i);
                    String tmp = String.valueOf(me.getKey()).toLowerCase();
                    switch (tmp) {
                        case "name":
                            matcher = pattern.matcher(tmpModelItem.getName().toLowerCase());
                            break;
                        default:
                            throw new InvalidModelItemException();
                    }
                    if (!matcher.matches()) {
                        restItems.remove(i);
                    } else {
                        i++;
                    }
                }
            }
        }

        return restItems;
    }
}
